package com.frinder.data.base;

import com.google.common.base.Strings;
import com.mongodb.WriteResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

import java.lang.reflect.ParameterizedType;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @ClassName AbstractService
 * @Description 数据操作实现基类
 * @Author frinder
 * @Date 2020/8/5 11:17
 * @Verion 1.0
 */
@Slf4j
@EnableMongoRepositories(basePackages = "com.frinder.data.domain.dao")
public class AbstractService<T> implements IService<T> {

    @Autowired
    private IMongoRepository<T> mongoRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    /** 当前泛型真实类型的Class */
    private Class<T> modelClass;

    public AbstractService() {
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        modelClass = (Class<T>) pt.getActualTypeArguments()[0];
    }

    @Override
    public void save(T target) {
        mongoRepository.save(target);
    }

    @Override
    public int update(String id, T target) {
        Query query = Query.query(Criteria.where("id").is(id));
        Update update = new Update();
        // 使用反射方式将Entity转换成K-V
        // V必须为包装类型，默认为null，否则可能导致值设置错误
        Map<Object, Object> fields = new BeanMap(target);
        String key;
        Object val;
        for (Map.Entry<Object, Object> entry : fields.entrySet()) {
            key = String.valueOf(entry.getKey());
            if ("id".equalsIgnoreCase(key)) {
                continue;
            }
            if ("class".equalsIgnoreCase(key)) {
                continue;
            }
            val = entry.getValue();
            if (Objects.isNull(val)) {
                continue;
            }
            update.set(key, val);
        }
        WriteResult result = mongoTemplate.updateFirst(query, update, modelClass);
        log.info("更新结果：{}", result);
        return result.getN();
    }

    @Override
    public void delete(String id) {
        mongoRepository.delete(id);
    }

    @Override
    public void delete(List<String> ids) {
        ids.parallelStream().forEach(id -> delete(id));
    }

    @Override
    public List<T> listAll() {
        return mongoRepository.findAll();
    }

    @Override
    public List<T> list(Query query) {
        return mongoTemplate.find(query, modelClass);
    }

    @Override
    public T findById(String id) {
        return mongoRepository.findById(id);
    }

    @Override
    public T findBySid(String sid) {
        return mongoRepository.findBySid(sid);
    }

    @Override
    public PageInfo<T> page(PageWrapper<T> page) {
        PageInfo<T> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page.getPage());
        pageInfo.setPageSize(page.getSize());
        // 总记录数
        Query query = createCountQuery(page);
        long count = mongoTemplate.count(query, modelClass);
        pageInfo.setTotal(count);
        // 分页记录
        query = createPageQuery(query, page);
        List<T> data = mongoTemplate.find(query, modelClass);
        pageInfo.setList(data);
        return pageInfo;
    }

    /**
     * @param page
     * @return
     */
    private Query createCountQuery(PageWrapper<T> page) {
        Query query = new Query();
        try {
            Criteria criteria = new Criteria();
            Object target = page.getTarget();
            if (null != target) {
                Map<Object, Object> fields = new BeanMap(target);
                String key;
                Object v;
                for (Map.Entry<Object, Object> entry : fields.entrySet()) {
                    key = String.valueOf(entry.getKey());
                    if ("class".equalsIgnoreCase(key)) {
                        continue;
                    }
                    v = entry.getValue();
                    if (page.getEqualFields().contains(key)) {
                        if (null == v) {
                            continue;
                        }
                        criteria.and(key).is(v);
                    } else if (page.getNotEqualFields().contains(key)) {
                        if (null == v) {
                            continue;
                        }
                        criteria.and(key).ne(v);
                    } else if (page.getInFields().containsKey(key)) {
                        criteria.and(key).in(page.getInFields().get(key));
                    } else if (page.getNotInFields().containsKey(key)) {
                        criteria.and(key).nin(page.getNotInFields().get(key));
                    } else if (page.getGreaterThanFields().containsKey(key)) {
                        criteria.and(key).gt(page.getGreaterThanFields().get(key));
                    } else if (page.getGreaterThanOrEqualFields().containsKey(key)) {
                        criteria.and(key).gte(page.getGreaterThanOrEqualFields().get(key));
                    } else if (page.getLessThanFields().containsKey(key)) {
                        criteria.and(key).lt(page.getLessThanFields().get(key));
                    } else if (page.getLessThanOrEqualFields().containsKey(key)) {
                        criteria.and(key).lte(page.getLessThanOrEqualFields().get(key));
                    } else {
                        if (null == v) {
                            continue;
                        }
                        if (v instanceof String) {
                            if (!Strings.isNullOrEmpty(v.toString())) {
                                criteria.and(key).regex(String.format("%s", v));
                            }
                        } else if (v instanceof Date) {
                        } else {
                            criteria.and(key).is(v);
                        }
                    }
                }
                query.addCriteria(criteria);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return query;
    }

    /**
     * 分页查询
     *
     * @param query
     * @param page
     * @return
     */
    private Query createPageQuery(Query query, PageWrapper<T> page) {
        query.with(new Sort(page.getOrders().stream()
                .map(order -> new Sort.Order(order.getStrategy().get(), order.getField()))
                .collect(Collectors.toList())
        ));
        query.skip(page.getSize() * (page.getPage() - 1)).limit(page.getSize());
        return query;
    }

}
